#pragma once

namespace Melting
{
	namespace Parallel
	{ 
		//--------------------------------------------------------------------------------------------------------------------
		typedef ThreadFunctionReturn (*ThreadFunctionPtr) (void*);
		//--------------------------------------------------------------------------------------------------------------------
		const ThreadHandle    INVALID_THREAD_HANDLE    = 0;
		    
		//--------------------------------------------------------------------------------------------------------------------
		class Thread
		{
		public:
			Thread( size_t _iStackSize = 0 );
			virtual ~Thread( void );

			bool            				Kill		( void );
			bool            				Wait		( void );
			virtual bool    				Run			( void );
			virtual bool    				Run			( ThreadFunctionPtr _pFunction );
			virtual bool    				Run			( ThreadFunctionPtr _pFunction, void* _pData );
			void            				SetFunction	( ThreadFunctionPtr _pFunction );
			void            				SetData		( void* _pData );
			void*           				GetData		( void ) const;
			bool            				IsActive	( void ) const;
			ThreadHandle    				GetHandle	( void ) const;
			size_t          				GetStackSize( void ) const;
			
			/// Specify on which cpu logical cores the thread associate to this object is OK to run - return true on success -- WARNING call this only after calling Run()!
			bool							SetAffinity	( UINT32 _iMask );

			/// Get the ThreadID of the calling thread
			inline static ThreadID			GetCurrentThreadID( void );
			/// Get the ThreadHandle of the calling thread
			inline static ThreadHandle		GetCurrentThreadHandle( void );
			/// Specify on which cpu logical cores the calling thread is OK to run - return true on success
			inline static bool				SetCurrentThreadAffinity( UINT32 _iMask );

		protected:
			ThreadHandle        mHandle;		//Thread handle
			size_t				miStackSize;
			bool                mbActive;		//activity indicator
			ThreadFunctionPtr   mpFunction;
			void*               mpData;
		};
		
	#ifdef WIN32

		//----------------------------------------------------------------
		//----------------------------------------------------------------
		ThreadID Thread::GetCurrentThreadID( void )
		{
			return ::GetCurrentThreadId(); 
		}

		//----------------------------------------------------------------
		//----------------------------------------------------------------
		ThreadHandle Thread::GetCurrentThreadHandle( void )
		{
			return ::GetCurrentThread();
		} 
		//----------------------------------------------------------------
		//----------------------------------------------------------------
		bool Thread::SetCurrentThreadAffinity( UINT32 _iMask )
		{
			return 0 != SetThreadAffinityMask( GetCurrentThreadHandle(), _iMask );
		}
		//----------------------------------------------------------------
		//----------------------------------------------------------------
	#else	// PTHREAD / Linux
	 	
		//----------------------------------------------------------------
		//----------------------------------------------------------------
		ThreadID Thread::GetCurrentThreadID( void )
		{
			return gettid(); // linux !
		}

		//----------------------------------------------------------------
		//----------------------------------------------------------------
		ThreadHandler Thread::GetCurrentThreadHandle( void )
		{
			return pthread_self( void );
		}
		//----------------------------------------------------------------
		//----------------------------------------------------------------
		bool Thread::SetCurrentThreadAffinity( UINT32 _iMask )
		{
			cpu_set_t cpuset;
			
			CPU_ZERO(&cpuset);

			// max for 32 cpu logical core !
			for( int i = 0; i < 32; i++ )
			{
				int iCoreBit = (1 << i):

				if( (_iMask & iCoreBit) > 0 )
				{
					CPU_SET( iCoreBit, &cpuset );
				}
			}

			return 0 == pthread_setaffinity_np( GetCurrentThreadHandle(), sizeof(cpu_set_t), &cpuset);
		}
	#endif
	}// namespace Parallel
}// namespace Melting